﻿// Inner Fire 游戏引擎库
// DirectX UI - 基础定义类
// 所属层级：第1层，基础结构类
//
// Copyright (c) 2024 by 尔西大帝. All rights reserved.
//
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-12-28

#include "ui_def.h"

namespace ifire::dx {

UIAlign UIAlign::GetAlign(int grid_num) {
  switch (grid_num) {
  case 1:
    return UIALIGN_BOTTOM_LEFT;
  case 2:
    return UIALIGN_BOTTOM_CENTER;
  case 3:
    return UIALIGN_BOTTOM_RIGHT;
  case 4:
    return UIALIGN_MID_LEFT;
  case 5:
    return UIALIGN_MID_CENTER;
  case 6:
    return UIALIGN_MID_RIGHT;
  case 7:
    return UIALIGN_TOP_LEFT;
  case 8:
    return UIALIGN_TOP_CENTER;
  case 9:
    return UIALIGN_TOP_RIGHT;
  }
  return UIALIGN_MID_CENTER;
}

XMFLOAT2 UIAlign::CalcTextStart(XMFLOAT2 size, const UIAlign& align,
    XMFLOAT2 parent_position, XMFLOAT2 parent_size) {

  XMFLOAT2 pos{};
  XMFLOAT2 half{parent_size.x * 0.5f, parent_size.y * 0.5f};

  if (align.horizontal == HorizontalAlignment::LEFT) {
    pos.x = parent_position.x - half.x;
  } else if (align.horizontal == HorizontalAlignment::CENTER) {
    pos.x = parent_position.x - size.x * 0.5f;
  } else if (align.horizontal == HorizontalAlignment::RIGHT) {
    pos.x = parent_position.x + half.x - size.x;
  }

  if (align.vertical == VerticalAlignment::TOP) {
    pos.y = parent_position.y - half.y;
  } else if (align.vertical == VerticalAlignment::MID) {
    pos.y = parent_position.y - size.y * 0.5f;
  } else if (align.vertical == VerticalAlignment::BOTTOM) {
    pos.y = parent_position.y + half.y - size.y;
  }

  return pos;
}

UIWidget::UIWidget()
    : position(0.0f, 0.0f)
    , size(0.0f, 0.0f)
    , scale(1.0f, 1.0f)
    , align(UIALIGN_MID_CENTER)
    , zindex(0)
    , anchor(0.5f, 0.5f) {}

UIWidget::UIWidget(const XmlNode& node) {
  position = node("Position");
  size = node("Size");
  scale = node("Scale", XMFLOAT2(1.0f, 1.0f));
  align = UIAlign::GetAlign(node("Align"));
  zindex = node("ZIndex");
  anchor = node("Anchor", XMFLOAT2(0.5f, 0.5f));
}

XMFLOAT2 UIWidget::GetPosition(const XMFLOAT2& parent_position,
    const XMFLOAT2& parent_size, const XMFLOAT2& parent_scale) const {
  XMFLOAT2 pos{};
  XMFLOAT2 half{parent_size.x * 0.5f * parent_scale.x,
      parent_size.y * 0.5f * parent_scale.y};

  if (align.horizontal == HorizontalAlignment::LEFT) {
    pos.x = parent_position.x - half.x + position.x;
  } else if (align.horizontal == HorizontalAlignment::CENTER) {
    pos.x = parent_position.x + position.x;
  } else if (align.horizontal == HorizontalAlignment::RIGHT) {
    pos.x = parent_position.x + half.x - position.x;
  }

  if (align.vertical == VerticalAlignment::TOP) {
    pos.y = parent_position.y - half.y + position.y;
  } else if (align.vertical == VerticalAlignment::MID) {
    pos.y = parent_position.y + position.y;
  } else if (align.vertical == VerticalAlignment::BOTTOM) {
    pos.y = parent_position.y + half.y - position.y;
  }

  // 以上的项目是得到中间的位置。然后根据Anchor来对这个中点进行偏移
  float width = size.x * scale.x * parent_scale.x;
  float height = size.y * scale.y * parent_scale.y;
  float xadd = -width * anchor.x + width * 0.5f;
  float yadd = -height * anchor.y + height * 0.5f;

  return XMFLOAT2(pos.x + xadd, pos.y + yadd);
}

UIWidget UIWidget::CalcWidget(const UIWidget& parent_widget) const {
  UIWidget widget{};
  widget.align = align;
  widget.zindex = parent_widget.zindex + zindex;
  widget.position = GetPosition(
      parent_widget.position, parent_widget.size, parent_widget.scale);
  widget.size = size;
  widget.scale = XMFLOAT2(
      scale.x * parent_widget.scale.x, scale.y * parent_widget.scale.y);

  // TODO：这里遇到一个BUG，目前不知道怎么解决，或者说，解决的方式太痛苦了。
  // 当我进行缩放的时候，比如说，一个Label，离一个Button的底部有10像素。我其它的东西都能计算对。
  // 但是唯独这个10像素没有办法计算正确。也就是说，这10像素不受缩放的影响，永远是10像素。
  // 如果我在GetPosition增加了对于Position的缩放，这个会影响到Canvas中的定位。
  // 换句话说，也许Canvas中的定位应该不受影响，才能导致定位的正确。
  // 我需要一套非常复杂的规则，才能导致缩放的正确。现在暂时不进行这个优化。能用就行。

  return widget;
}

bool UIWidget::IsHit(XMFLOAT2 pos) const {
  float width = size.x * scale.x;
  float height = size.y * scale.y;

  float left = position.x - width * 0.5f;
  float right = position.x + width * 0.5f;
  float top = position.y - height * 0.5f;
  float bottom = position.y + height * 0.5f;

  return (pos.x >= left && pos.x < right && pos.y >= top && pos.y < bottom);
}

float UIWidget::MapZ(float zindex) {
  // Zindex的值越大，越在前面。所以0对应的是1.0的位置，1000.0f对应的是0的位置
  // 最后需要设置一个[0,1]到[0,0.9]的印射，因为1.0是不会被判断通过的。
  return (MAX_ZINDEX - zindex) / MAX_ZINDEX * 0.9f;
}

} // namespace ifire::dx